package endPoint.codeLayer;

/**
 * Implementa un oggetto in grado di codificare stringhe secondo il codice di Hamming.
 * Spesso i valori binari sono espressi utilizzando variabili booleane. In questi casi il valore true 
 * è fatto corrispondere al bit 1 e il valore false al bit 0. 
 * Il numero di bit di controllo da usare è calcolato contando il numero di caratteri della word da
 * codificare che si trovano in corrispondenza di un indice di posizione
 * multiplo di 2 (considerando 1 come primo indice di posizione). Per la
 * codifica è usata una matrice generatrice trasposta che pone i bit di
 * controllo in corrispondenza delle posizioni di indice multiplo di 2 (con 1
 * come primo indice).
 *
 * @author calvelli
 */
public class HammingCoder implements Coder{ 

    /**
     * Codifica la stringa ricevuta come parametro applicando HammingCoder.
     * Calcola il numero di bit di controllo da utilizzare in base alla
     * lunghezza della word da codificare. I bit di controllo sono posizionati
     * in corrispondenza delle posizioni di indice multiplo di 2 (considerando 1
     * come indice della prima posizione).
     *
     * @param word : stringa da codificare.
     * @return stringa risultante dalla codifica.
     */
    public String code(String word) {
        int infoBits = word.length();
        boolean[][] matrix = createGeneratingMatrix(infoBits);
        boolean[] resultBoolean = new boolean[matrix.length];
        String result = new String();

        for (int i = 0; i < matrix.length; i++) {
            resultBoolean[i] = xor(matrix[i], word);
        }//for i

        for (int i = 0; i < resultBoolean.length; i++) {
            if (resultBoolean[i]) {
                result = result + "1";
            } else {
                result = result + "0";
            }
        }//for i

        return result;
    }//code

    /**
     * Conta il numero di bit pari a 1 contenuti in row in corrispondenza di bit
     * pari a 1. in row.
     *
     * @param row : vettore booleano
     * @param word : stringa
     * @return true se il numero di 1 trovati è dispari, 0 altrimenti.
     */
    public boolean xor(boolean[] row, String word) {
        int size = row.length;
        boolean[] and = new boolean[size];
        boolean result = false;
        for (int i = 0; i < size; i++) {
            if (word.charAt(i) == '1') {
                and[i] = row[i];
            } else {
                and[i] = false;
            }
            result ^= and[i];
        }//for

        return result;

    }//xor

    /**
     * Crea la matrice generatrice per codificare parole di lunghezza pari al
     * valore dell'intero in input.
     *
     * @param infoBits : numero di bit di informazione di cui controllare la
     * parità
     * @return matrice booleana
     */
    public boolean[][] createGeneratingMatrix(int infoBits) {
        boolean[][] matrix = null;
        int positionNextControlBit = 1, indexIdentityMatrix = 0;
        int controlBits = 0; //numero bit di controllo da inserire
        while (positionNextControlBit <= infoBits) {
            controlBits++;
            positionNextControlBit *= 2;
        }//while(nextControlBit <= n)          

        //costruisco la trasposta della matrice di generazione
        int codeLength = infoBits + controlBits;
        matrix = new boolean[codeLength][infoBits];
        positionNextControlBit = 1;
        boolean[] currentPermutation = new boolean[infoBits];
        for (int i = 0; i < controlBits; i++) {
            currentPermutation[i] = true;
        }//for

        //riempio matrice
        for (int i = 0; i < codeLength; i++) {
            if (i == (positionNextControlBit - 1)) {
                matrix[i] = nextPermutation(currentPermutation);
                currentPermutation = matrix[i];
                positionNextControlBit *= 2;
            } else {

                matrix[i][indexIdentityMatrix] = true;
                indexIdentityMatrix++;
            }

        }//for row         

        return matrix;
    }//createMatrix

     /**
     * Effettua uno shift destro ciclico del vettore ricevuto. E' usato per
     * calcolare l'insieme di bit d'informazione la cui parità sarà controllata
     * da un certo bit di controllo.
     *
     * @param currentPermutation : vettore booleano da shiftare.
     * @return un nuovo vettore booleano shiftato.
     */
    private boolean[] nextPermutation(boolean[] currentPermutation) {
        int size = currentPermutation.length;
        boolean[] result = new boolean[size];
        for (int i = 1; i < size; i++) {
            result[i] = currentPermutation[i - 1];
        }//for
        result[0] = currentPermutation[size - 1];
        return result;
    }//nextPermutation
    

//    public static void main(String[] a) {
//        HammingCoder h = new HammingCoder();
//        //System.out.println(h.code("1011"));
//        boolean[][] g = h.createGeneratingMatrix(4);
//        System.out.println("G");
//        for (int i = 0; i < g.length; i++) {
//            for (int j = 0; j < g[0].length; j++) {
//                if (g[i][j]) {
//                    System.out.print("1" + " ");
//                } else {
//                    System.out.print("0" + " ");
//                }
//            }
//            System.out.println();
//        }
//
//        System.out.println("H");
//        boolean[][] m = h.createParityMatrix(g);
//        for (int i = 0; i < m.length; i++) {
//            for (int j = 0; j < m[0].length; j++) {
//                if (m[i][j]) {
//                    System.out.print("1" + " ");
//                } else {
//                    System.out.print("0" + " ");
//                }
//            }
//            System.out.println();
//        }
//        System.out.println("spazio");
//
//        String word = h.code("1011");
//        System.out.println(word);
//        System.out.println(h.decode(g, "0110011"));
//    }//main

}
